Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

35장. 경로 · 호스트 기반 라우팅으로 서비스 쪼개기

이 장에서 말하고자 하는 것

앞 장에서 우리는 ALB의 부품 — 리스너 · 규칙 · 타깃 그룹 — 을 봤다.

이 장은 그 부품을 가지고

하나의 ALB로 여러 마이크로서비스를 어떻게 나눠 받는가

를 본다.

마이크로서비스 구조에서 ALB가 가장 빛나는 지점이다.


1. 출발 — 모놀리스에서 MSA로

작은 서비스에는 보통 ALB 한 대 + 서버 한 묶음이면 충분하다.

사용자 → ALB → web 서버 (모놀리스)

서비스가 커지면 기능별로 나누고 싶어진다.

주문, 사용자, 결제, 알림 ...

각각을 다른 서비스로 띄우면

사용자 → ALB → orders 서비스
                users 서비스
                payments 서비스

이때 ALB가 어떻게 분기할지 결정해야 한다.


2. 두 가지 분기 방법

ALB가 서비스를 나누는 방법은 크게 두 가지다.

  • 경로 기반(Path-based)
  • 호스트 기반(Host-based)

둘은 함께 써도 된다.


3. 경로 기반 라우팅

같은 도메인 안에서 경로로 서비스를 나눈다.

api.example.com/orders/*   → orders 서비스
api.example.com/users/*    → users 서비스
api.example.com/payments/* → payments 서비스
api.example.com/*           → web (기본)

특징:

  • 클라이언트는 도메인 하나만 알면 된다
  • 인증서도 한 장이면 된다
  • 프론트엔드와 같은 도메인이라 CORS가 단순해진다

작은 ~ 중간 규모 MSA에 적합하다.


4. 호스트 기반 라우팅

서브도메인으로 나눈다.

orders.example.com   → orders 서비스
users.example.com    → users 서비스
payments.example.com → payments 서비스
admin.example.com    → admin 서비스

특징:

  • 서비스 경계가 명확하다
  • DNS 와 인증서 관리 부담이 늘어난다 (ACM 와일드카드로 완화 가능)
  • 보안 경계 분리에 유리하다

내부망과 외부망이 섞인 환경이나
관리자/사용자 영역 분리에 자주 쓴다.


5. 둘을 함께 쓰는 패턴

운영에서는 보통 섞어 쓴다.

api.example.com/orders/*    → orders
api.example.com/users/*     → users
api.example.com/payments/*  → payments

admin.example.com/*         → admin

외부 사용자용 API는 경로로 나누고
관리자나 별도 도메인이 필요한 영역은 호스트로 분리


6. 규칙은 우선순위대로

ALB의 규칙은 우선순위(priority)대로 평가된다.

priority 10 : path = /api/orders/*    → TG-orders
priority 20 : path = /api/users/*     → TG-users
priority 30 : path = /api/payments/*  → TG-payments
default     : *                       → TG-web

priority가 작을수록 먼저 평가된다.

가장 구체적인 규칙을 앞에 둔다.

/api/orders/special   ← 먼저
/api/orders/*         ← 그다음
/api/*                ← 그 다음
/*                    ← 마지막

7. 서비스마다 타깃 그룹은 따로

이게 가장 중요한 원칙이다.

orders   → TG-orders   → orders 서비스
users    → TG-users    → users 서비스
payments → TG-payments → payments 서비스

한 ALB · 여러 타깃 그룹 · 각 타깃 그룹은 한 서비스

이렇게 분리해야

  • 서비스별 헬스 체크가 독립된다
  • 서비스별 스케일링이 분리된다
  • 서비스별 배포가 분리된다
  • 한 서비스 장애가 다른 서비스에 안 번진다

8. 우리 서비스에서

척추에 점을 찍어보자.

[사용자]
    ↓ DNS
[CloudFront]
    ↓
[API Gateway]
    ↓
[ALB]   ← 여기서 경로로 4개로 나뉜다
 ├─ /api/orders/*   → ECS "orders"
 ├─ /api/users/*    → ECS "users"
 ├─ /api/payments/* → ECS "payments"
 └─ /*              → ECS "web"

이 ALB가 사실상

사용자가 만든 요청을 마이크로서비스로 흩어 보내는 분배기

역할을 한다.


9. 직접 확인해보기 — CLI

규칙 추가

aws elbv2 create-rule \
  --listener-arn <listener-arn> \
  --priority 10 \
  --conditions Field=path-pattern,Values='/api/orders/*' \
  --actions Type=forward,TargetGroupArn=<tg-orders-arn>

규칙 우선순위 변경

aws elbv2 set-rule-priorities \
  --rule-priorities RuleArn=<rule-arn>,Priority=5

어떤 규칙에 매칭됐는지 확인

ALB 액세스 로그를 S3에 켜두면
요청별로 어떤 타깃 그룹에 갔는지 다 남는다.

target_group_arn = arn:aws:elasticloadbalancing:.../TG-orders/...

10. 코드로는 이렇게 생겼다 — Terraform

서비스 3개를 한 ALB로 분기하는 모양이다.

# 공통 ALB와 리스너는 앞 장에 있다고 가정

locals {
  services = {
    orders   = { path = "/api/orders/*",   port = 8081 }
    users    = { path = "/api/users/*",    port = 8082 }
    payments = { path = "/api/payments/*", port = 8083 }
  }
}

resource "aws_lb_target_group" "svc" {
  for_each = local.services

  name        = "tg-${each.key}"
  port        = each.value.port
  protocol    = "HTTP"
  target_type = "ip"
  vpc_id      = aws_vpc.main.id

  health_check {
    path = "/health"
  }
}

resource "aws_lb_listener_rule" "svc" {
  for_each = local.services

  listener_arn = aws_lb_listener.https.arn
  priority     = 100 + index(keys(local.services), each.key)

  condition {
    path_pattern {
      values = [each.value.path]
    }
  }

  action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.svc[each.key].arn
  }
}

서비스를 추가할 때 services 맵에 한 줄만 더하면 된다.


11. 이렇게 쓰면 망한다 — 안티패턴

안티패턴 1. 정규식 같은 복잡한 매칭을 ALB에 떠넘긴다

ALB의 path_pattern은 *, ? 정도만 지원한다.
복잡한 라우팅을 ALB에 넣으면 빠르게 한계가 온다.

복잡한 매칭은 애플리케이션 또는 API Gateway로

안티패턴 2. 같은 path를 여러 priority에 중복으로 둔다

priority 10 : /api/*    → TG-monolith
priority 20 : /api/v2/* → TG-v2

10이 먼저 잡아서 20은 영원히 실행되지 않는다.

더 구체적인 규칙을 작은 priority에 둔다

안티패턴 3. 서비스를 늘릴 때마다 ALB를 새로 만든다

ALB 자체가 비용이 든다.
한 ALB에 규칙 100개까지는 무리 없이 들어간다.

한 ALB로 모으되, 너무 많아지면 도메인 단위로 나눈다

안티패턴 4. 모든 서비스를 한 타깃 그룹에 묶고 path만 다르게 둔다

타깃 그룹은 “같은 역할의 서버 묶음” 이다.
역할이 다른 서비스를 같은 TG에 두면

  • 헬스 체크가 의미를 잃고
  • 스케일링이 묶이고
  • 장애가 서로 번진다

12. 한 줄로 정리

하나의 ALB에서 경로와 호스트로 트래픽을 나눠
각 마이크로서비스의 타깃 그룹으로 보내는 것이 MSA의 진입 구조다


13. 이 장의 핵심 정리

  1. 경로 기반과 호스트 기반은 ALB에서 서비스를 나누는 두 축이다.
  2. 두 방식은 함께 써도 된다.
  3. 서비스마다 타깃 그룹을 반드시 따로 둔다.
  4. priority는 가장 구체적인 규칙을 앞에 둔다.
  5. 한 ALB로 수십 개 서비스까지는 충분히 운영된다.
  6. 복잡한 라우팅은 ALB가 아니라 API Gateway / 애플리케이션 단계로 옮긴다.